﻿using UnityEngine;
using System.Collections;

using Sfs2X;
using Sfs2X.Core;
using Sfs2X.Requests;
using Sfs2X.Entities;
using Sfs2X.Entities.Data;
using Sfs2X.Logging;

/// <summary>
/// Smartfox Network Manager.
/// </summary>
[AddComponentMenu("wsLibrary/Network/Smartfox Network Manager")]
public class wsSmartfoxNetManager : MonoBehaviour
{
	#region Singleton Pattern
	/**********************************
	 * singleton
	 * *******************************/
	/// <summary>
	/// Get Singleton Instance.
	/// </summary>
	public static wsSmartfoxNetManager Instance
	{
		get
		{
			return wsSingletonObject<wsSmartfoxNetManager>.Instance;
		}
	}

	/// <summary>
	/// Awake this instance.
	/// </summary>
	void Awake()
	{
		wsSingletonObject<wsSmartfoxNetManager>.Awake(gameObject);
	}	
	#endregion

	/**********************************
	 * member
	 * *******************************/
	/// <summary>
	/// config file path.
	/// </summary>
	public string configFile = "Config/config_sfs2x.xml";
	/// <summary>
	/// load server info from config file.
	/// </summary>
	public bool useConfigFile = false;
	/// <summary>
	/// use debug mode with smartfox server.
	/// </summary>
	public bool useDebugMode = false;
	/// <summary>
	/// use external join handler
	/// </summary>
	public bool useJoinHandler = false;
	/// <summary>
	/// auto connect to server.
	/// </summary>
	public bool autoStart = false;
	/// <summary>
	/// send message to keep connection with server.
	/// </summary>
	public float keepConnectionTime = 60;
	/// <summary>
	/// last time when keep connection message is sended.
	/// </summary>
	private float lastTimeToSendMessage = 0;
	/// <summary>
	/// server name.
	/// </summary>
	public string serverName = "casino.uzoo.in";
	/// <summary>
	/// server port.
	/// </summary>
	public int serverPort = 9933;
	/// <summary>
	/// zone name.
	/// </summary>
	public string zoneName = "EnjoyCasino";
	/// <summary>
	/// user name.
	/// </summary>
	public string userName = "fox";
	/// <summary>
	/// user password.
	/// </summary>
	public string userPass = "";
	/// <summary>
	/// room name.
	/// </summary>
	public string roomName = "CD-BJS5";
	/// <summary>
	/// room password.
	/// </summary>
	public string roomPass = "gkdlekzh";
	/// <summary>
	/// create room.
	/// </summary>
	public bool createRoom = false;

	/// <summary>
	/// main instance to communicate to server.
	/// </summary>
	private SmartFox smartFox;

	/// <summary>
	/// is logedd in.
	/// </summary>
	[System.NonSerialized]
	public bool isLoggedIn = false;
	/// <summary>
	/// is joined room.
	/// </summary>
	[System.NonSerialized]
	public bool isJoinRoom = false;

	/**********************************
	 * delegate & event
	 * *******************************/
	public delegate void SFSEventBase();
	public delegate void SFSEventConnection(bool success);
	public delegate void SFSEventLogin(Sfs2X.Entities.User user);
	public delegate void SFSEventJoinRoom(Sfs2X.Entities.Room room);
	public delegate void SFSEventPublicMessage(Sfs2X.Entities.User user, string message);
	public delegate void SFSEventExtensionMessage(string command, ISFSObject inData);
	public delegate void SFSEventError(string error);

	public event SFSEventConnection OnConnectionEvent;
	public event SFSEventBase OnConnectionLostEvent;
	public event SFSEventLogin OnLoginEvent;
	public event SFSEventLogin OnLogoutEvent;
	public event SFSEventBase OnLoginErrorEvent;
	public event SFSEventJoinRoom OnCreateRoomEvent;
	public event SFSEventJoinRoom OnJoinRoomEvent;
	public event SFSEventPublicMessage OnPublicMessageEvent;
	public event SFSEventExtensionMessage OnExtensionResponseEvent;
	public event SFSEventError OnTrace;


	/**********************************
	 * method
	 * *******************************/

	void Start()
	{
		if (autoStart)
			ConnectServer();
	}

	/// <summary>
	/// overwrite default user info.
	/// </summary>
	/// <param name="userName"></param>
	/// <param name="userPass"></param>
	/// <param name="roomName"></param>
	/// <param name="roomPass"></param>
	/// <param name="createRoom"></param>
	public void SetInfo(string userName, string userPass, string roomName, string roomPass, bool createRoom)
	{
		this.userName = userName;
		this.userPass = userPass;
		this.roomName = roomName;
		this.roomPass = roomPass;
		this.createRoom = createRoom;
	}

	/// <summary>
	/// connect to server and change user.
	/// </summary>
	/// <param name="userName"></param>
	/// <param name="userPass"></param>
	public void ConnectServer(string userName, string userPass)
	{
		this.userName = userName;
		this.userPass = userPass;
		//this.useExternalHandler = true;

		this.ConnectServer();
	}

	/// <summary>
	/// connect to server.
	/// </summary>
	public void ConnectServer()
	{
		// In a webplayer (or editor in webplayer mode) we need to setup security policy negotiation with the server first
		if (Application.isWebPlayer || Application.isEditor)
		{
			if (!Security.PrefetchSocketPolicy(serverName, serverPort, 500))
			{
				Debug.LogError("Security Exception. Policy file load failed!");
			}
		}

		// Lets connect
		smartFox = new SmartFox(useDebugMode); // true is debug mode on.
		smartFox.ThreadSafeMode = true;

		// Register callback delegate
		smartFox.AddEventListener(SFSEvent.CONFIG_LOAD_SUCCESS, OnConfigLoad);
		smartFox.AddEventListener(SFSEvent.CONFIG_LOAD_FAILURE, OnConfigFail);
		smartFox.AddEventListener(SFSEvent.CONNECTION, OnConnection);
		smartFox.AddEventListener(SFSEvent.CONNECTION_LOST, OnConnectionLost);
		smartFox.AddEventListener(SFSEvent.LOGIN, OnLogin);
		smartFox.AddEventListener(SFSEvent.LOGIN_ERROR, OnLoginError);
		smartFox.AddEventListener(SFSEvent.LOGOUT, OnLogout);
		smartFox.AddEventListener(SFSEvent.ROOM_ADD, OnRoomAdded);
		smartFox.AddEventListener(SFSEvent.ROOM_CREATION_ERROR, OnRoomCreationError);
		smartFox.AddEventListener(SFSEvent.ROOM_JOIN, OnJoinRoom);
		smartFox.AddEventListener(SFSEvent.ROOM_JOIN_ERROR, OnJoinRoomError);
		smartFox.AddEventListener(SFSEvent.PUBLIC_MESSAGE, OnPublicMessage);
		smartFox.AddEventListener(SFSEvent.EXTENSION_RESPONSE, OnExtensionResponse);

		smartFox.AddLogListener(LogLevel.DEBUG, OnDebugMessage);

		if (useConfigFile)
		{
			string configFilePath = Application.dataPath + "/" + configFile;
			trace("Try to access config file:" + configFilePath);
			smartFox.LoadConfig(configFilePath);
		}
		else
		{
			smartFox.Connect(serverName, serverPort);
		}

		//this.Send( new LogoutRequest() );
	}

	//------------------------------------------------------------------------------------
	// As Unity is not thread safe, we process the queued up callbacks every physics tick
	//------------------------------------------------------------------------------------
	void FixedUpdate()
	{
		if (smartFox != null)
			smartFox.ProcessEvents();

		if (Time.time - lastTimeToSendMessage > keepConnectionTime)
		{
			ISFSObject outData = new SFSObject();
			outData.PutNull("common_null");
			this.Send(new ExtensionRequest("Common.KeepConnection", outData));
		}
	}

	void OnApplicationQuit()
	{
		if (smartFox != null && smartFox.IsConnected)
			smartFox.Disconnect();
	}

	//----------------------------------------------------------
	// Handle connection response from server
	//----------------------------------------------------------
	void OnConfigLoad(BaseEvent e)
	{
		trace("Config file loaded");
		smartFox.Connect(smartFox.Config.Host, smartFox.Config.Port);
	}

	void OnConfigFail(BaseEvent e)
	{
		trace("Failed to load Config File");
		trace("Retry to access to server with default information.");
		smartFox.Connect(serverName, serverPort);
	}

	void OnConnection(BaseEvent e)
	{
		bool connectionSuccess = (bool)e.Params["success"];

		if (connectionSuccess)
		{
			trace("Connection success.");
			this.Send(new LoginRequest(userName, userPass, zoneName));
		}
		else
		{
			trace("Connection failed, Reason: " + (string)e.Params["errorMessage"]);

			string localSever = "127.0.0.1";
			if (serverName != localSever)
			{
				serverName = localSever;
				trace("Retry to access to local server.");
				smartFox.Connect(serverName, serverPort);
			}
		}

		// call the event handler.
		if (OnConnectionEvent != null)
			//OnConnectionEvent(e, smartFox);
			OnConnectionEvent(connectionSuccess);
	}


	void OnConnectionLost(BaseEvent e)
	{
		// Reset all internal states so we kick back to login screen
		isLoggedIn = false;
		isJoinRoom = false;

		// debug message.
		trace("Connection lost, Reason: " + (string)e.Params["reason"]);

		// call the event handler.
		if (OnConnectionLostEvent != null)
			//OnConnectionLostEvent(e, smartFox);
			OnConnectionLostEvent();
	}

	void OnLogin(BaseEvent e)
	{
		// set static variables.
		// Make sure we got in and then populate the room list string array
		isLoggedIn = true;

		// get user info.
		User loginUser = (User)e.Params["user"];

		// debug message.
		trace("Logged in successfully, user: " + loginUser);

		if (useJoinHandler == false)
		{
			this.JoinRoom(roomName, roomPass, createRoom);
		}

		// call the event handler.
		if (OnLoginEvent != null)
			//OnLoginEvent(e, smartFox);
			OnLoginEvent(loginUser);
	}

	void OnLoginError(BaseEvent e)
	{
		// debug message.
		trace("Login error: " + e.Params["errorMessage"]);

		// call the event handler.
		if (OnLoginErrorEvent != null)
			//OnLoginErrorEvent(e, smartFox);
			OnLoginErrorEvent();
	}

	void OnLogout(BaseEvent e)
	{
		// set static variables.
		isLoggedIn = false;
		isJoinRoom = false;

		// get user info.
		User logoutUser = (User)e.Params["user"];

		// debug message.
		trace("Logged out, user: " + logoutUser);

		// call the event handler.
		if (OnLogoutEvent != null)
			//OnLogoutEvent(e, smartFox);
			OnLogoutEvent(logoutUser);
	}

	void OnRoomAdded(BaseEvent e)
	{
		Room room = (Room)e.Params["room"];
		trace("Room " + room.Name + " created successfully");

		// call the event handler.
		if (OnCreateRoomEvent != null)
			OnCreateRoomEvent(room);
	}

	void OnRoomCreationError(BaseEvent e)
	{
		trace("An error occurred while attempting to create the Room: " + (string)e.Params["errorMessage"]);
	}

	void OnJoinRoom(BaseEvent e)
	{
		isJoinRoom = true;

		Room room = (Room)e.Params["room"];
		trace("Room " + room.Name + " joined successfully");

		// lock object sample.
		/*lock( messagesLocker )
		{
			// send public message.
			this.Send( new PublicMessageRequest("This is online casino game test") );
		
			// send extension message.
			ISFSObject outData = new SFSObject();
			outData.PutNull("blackjack_c2s");
			this.Send(new ExtensionRequest("BlackJack.Init", outData));
		}*/

		// call the event handler.
		if (OnJoinRoomEvent != null)
			//OnJoinRoomEvent(e, smartFox);
			OnJoinRoomEvent(room);
	}

	void OnJoinRoomError(BaseEvent e)
	{
		trace("An error occurred while attempting to create the Room: " + (string)e.Params["errorMessage"]);
	}

	void OnPublicMessage(BaseEvent e)
	{
		User sender = (User)e.Params["sender"];
		string message = (string)e.Params["message"];

		trace("Public Msg : from " + sender.Name);

		// call the event handler.
		if (OnPublicMessageEvent != null)
			//OnPublicMessageEvent(e, smartFox);
			OnPublicMessageEvent(sender, message);
	}

	void OnExtensionResponse(BaseEvent e)
	{
		string cmd = (string)e.Params["cmd"];
		ISFSObject inData = (SFSObject)e.Params["params"];

		trace("Extension Msg : " + cmd);

		//		if( cmd == "BlackJack.GameStart" )
		//		{
		//			int[] card_list = inData.GetIntArray("card_list");
		//			
		//			foreach(int card in card_list)
		//				DebugOut("card value : " + card);
		//			
		//			ISFSObject outData = new SFSObject();
		//			outData.PutNull("blackjack_c2s");
		//			this.Send(new ExtensionRequest("BlackJack.GameTest", outData));
		//		}

		// call the event handler.
		if (OnExtensionResponseEvent != null)
			//OnExtensionResponseEvent(e, smartFox);
			OnExtensionResponseEvent(cmd, inData);
	}

	void OnDebugMessage(BaseEvent e)
	{
		trace("[SFS DEBUG] " + (string)e.Params["message"]);
	}

	public void Send(IRequest request)
	{
		this.smartFox.Send(request);
		this.lastTimeToSendMessage = Time.time;
	}

	/// <summary>
	/// create room and join to room
	/// </summary>
	/// <param name="name"></param>
	/// <param name="password"></param>
	/// <param name="createRoom"></param>
	public void JoinRoom(string name, string password, bool createRoom)
	{
		// Need to leave current room, if we are joined one
		if (smartFox.LastJoinedRoom == null)
		{
			if (createRoom)
			{
				trace("try to create room.");
				RoomSettings settings = new RoomSettings(name);
				settings.IsGame = true;
				settings.Password = password;
				this.Send(new CreateRoomRequest(settings, true));
			}
			else
			{
				trace("try to join room.");
				this.Send(new JoinRoomRequest(name, password));
			}
		}
		else
		{
			if (createRoom)
			{
				trace("exit and try to create room.");
				RoomSettings settings = new RoomSettings(name);
				settings.IsGame = true;
				settings.Password = password;
				this.Send(new CreateRoomRequest(settings, true, smartFox.LastJoinedRoom));
			}
			else
			{
				trace("exit and try to join room.");
				this.Send(new JoinRoomRequest(name, password, smartFox.LastJoinedRoom.Id));
			}
		}
	}

	/// <summary>
	/// trace message
	/// </summary>
	/// <param name="message"></param>
	public void trace(string message)
	{
		//DebugConsole.Log(message);

		if (OnTrace != null)
		{
			OnTrace(message);
		}
		else
		{
			Debug.Log(message);
		}
	}

	/// <summary>
	/// trace message
	/// </summary>
	/// <param name="message"></param>
	/// <param name="onlyDebug"></param>
	public void trace(string message, bool onlyDebug)
	{
		if (onlyDebug)
		{
			if (Debug.isDebugBuild)
				trace(message);
		}
		else
		{
			trace(message);
		}
	}
}
